Shell escape process arguments that are printed out
authorroot <root@localhost>
Sat, 2 May 2015 23:33:53 +0000 (01:33 +0200)
committerroot <root@localhost>
Tue, 2 Jun 2015 13:21:34 +0000 (15:21 +0200)
cargo build --verbose does output some command lines that cannot be
simply copy and pasted into the shell again. The problem is the
arguments which are output exactly like this: --feature="foo"

When pasted back into the shell, the shell will parse and remove the
double quotes. To counteract this, escape special shell characters when
printing commandlines. Cargo will print --feature=\"foo\" instead, which
can be pasted back into the shell.

src/cargo/util/mod.rs
src/cargo/util/process_builder.rs
src/cargo/util/shell_escape.rs [new file with mode: 0644]

index e29fa5844541d6f0cbb047f00419ab8d476ad72b..685e5fe472e2e62f7850369785e5e6bd8ef73a72 100644 (file)
@@ -31,5 +31,6 @@ pub mod toml;
 pub mod lev_distance;
 mod dependency_queue;
 mod sha256;
+mod shell_escape;
 mod vcs;
 mod mtime;
index 81d3b92c3eb909d5594fb1a43a0471bdf4147071..2411424f535d0193ba93c9eb888fd75c2f0f9cbc 100644 (file)
@@ -6,6 +6,7 @@ use std::path::Path;
 use std::process::{Command, Output};
 
 use util::{CargoResult, ProcessError, process_error};
+use util::shell_escape::shell_escape;
 
 #[derive(Clone, PartialEq, Debug)]
 pub struct ProcessBuilder {
@@ -20,7 +21,7 @@ impl fmt::Display for ProcessBuilder {
         try!(write!(f, "`{}", self.program.to_string_lossy()));
 
         for arg in self.args.iter() {
-            try!(write!(f, " {}", arg.to_string_lossy()));
+            try!(write!(f, " {}", shell_escape(arg.to_string_lossy())));
         }
 
         write!(f, "`")
diff --git a/src/cargo/util/shell_escape.rs b/src/cargo/util/shell_escape.rs
new file mode 100644 (file)
index 0000000..d02c173
--- /dev/null
@@ -0,0 +1,40 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use std::borrow::Cow;
+
+static SHELL_SPECIAL: &'static str = r#"\$'"`!"#;
+
+/// Escape characters that may have special meaning in a shell.
+pub fn shell_escape(s: Cow<str>) -> Cow<str> {
+    let escape_char = '\\';
+    // check if string needs to be escaped
+    let clean = SHELL_SPECIAL.chars().all(|sp_char| !s.contains(sp_char));
+    if clean {
+        return s
+    }
+    let mut es = String::with_capacity(s.len());
+    for ch in s.chars() {
+        if SHELL_SPECIAL.contains(ch) {
+            es.push(escape_char);
+        }
+        es.push(ch)
+    }
+    es.into()
+}
+
+#[test]
+fn test_shell_escape() {
+    assert_eq!(shell_escape("--aaa=bbb ccc".into()), "--aaa=bbb ccc");
+    assert_eq!(shell_escape(r#"--features="default""#.into()),
+                            r#"--features=\"default\""#);
+    assert_eq!(shell_escape(r#"'!\$` \\ \n"#.into()),
+                            r#"\'\!\\\$\` \\\\ \\n"#);
+}